{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1ea948bd",
   "metadata": {},
   "source": [
    "# Lesson 5: Filesystem modification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "351bffc5",
   "metadata": {},
   "source": [
    "**Objectives**: learn about OFRAK's filesystem unpacking and modification capabilities; modify a binary from within a filesystem and the filesystem itself"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "54fdd166",
   "metadata": {},
   "source": [
    "In this section, our boring old \"Hello, World!\" binary will be embedded within a SquashFS filesystem image, with the path `src/hello_world`:\n",
    "\n",
    "```\n",
    "squashfs_dir\n",
    "└── src\n",
    "    └── hello_world\n",
    "```\n",
    "\n",
    "Let's first create that image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b6915452",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ofrak_tutorial.helper_functions import HELLO_WORLD_SOURCE, create_binary\n",
    "\n",
    "!rm -rf squashfs_dir && mkdir -p squashfs_dir/src\n",
    "\n",
    "create_binary(HELLO_WORLD_SOURCE, \"squashfs_dir/src/hello_world\")\n",
    "\n",
    "!rm -f image.sqsh && mksquashfs squashfs_dir image.sqsh >/dev/null"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "385e53e0",
   "metadata": {},
   "source": [
    "Now we'll want to do a few things to this image:\n",
    "- modify the binary to output something even more fun and furry than in [Lesson 1](1_simple_string_modification.ipynb) 😼😼\n",
    "- modify the permission bits and extended attributes of the binary"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2542c969",
   "metadata": {},
   "source": [
    "First, let's see how to unpack and view the current SquashFS image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f7fe4aa6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using OFRAK Community License.\n"
     ]
    }
   ],
   "source": [
    "from ofrak import OFRAK\n",
    "\n",
    "ofrak = OFRAK()\n",
    "basic_context = await ofrak.create_ofrak_context()\n",
    "root_resource = await basic_context.create_root_resource_from_file(\"image.sqsh\")\n",
    "unpack_result = await root_resource.unpack_recursively()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed959bec",
   "metadata": {},
   "source": [
    "Which tags does our unpacked resource have?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "570aa12a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "File\n",
      "FilesystemEntry\n",
      "FilesystemRoot\n",
      "GenericBinary\n",
      "SquashfsFilesystem\n"
     ]
    }
   ],
   "source": [
    "for tag in sorted(root_resource.get_tags(), key=str):\n",
    "    print(tag)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19644ec2",
   "metadata": {},
   "source": [
    "`SquashfsFilesystem` looks like the most interesting tag for the operations we want to perform. Let's create a resource view of that type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f38d3d94",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ofrak.core import SquashfsFilesystem\n",
    "\n",
    "squashfs_view = await root_resource.view_as(SquashfsFilesystem)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca3bf49b",
   "metadata": {},
   "source": [
    "... And let's get the hello world binary from within it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "2c8cda3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "hello_world_program = await squashfs_view.get_entry(\"src/hello_world\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "096f870e",
   "metadata": {},
   "source": [
    "Okay. What are the current permission bits and extended attributes?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b3fe7cc1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "current permission bits: 100755\n",
      "current extended attributes: {}\n"
     ]
    }
   ],
   "source": [
    "print(f\"current permission bits: {hello_world_program.stat.st_mode:o}\")\n",
    "print(f\"current extended attributes: {hello_world_program.xattrs}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30c48b8e",
   "metadata": {},
   "source": [
    "We observe that our binary is possibly even more boring in its permissions and extended attributes than in what it outputs. Let's remedy all of this, in one go:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4443b240",
   "metadata": {},
   "outputs": [],
   "source": [
    "import stat\n",
    "\n",
    "from ofrak.core import BinaryPatchConfig, BinaryPatchModifier\n",
    "\n",
    "\n",
    "async def modify_squashfsed_program(ofrak_context, input_filename, output_filename):\n",
    "    root_resource = await ofrak_context.create_root_resource_from_file(input_filename)\n",
    "\n",
    "    await root_resource.unpack_recursively()\n",
    "    squashfs_view = await root_resource.view_as(SquashfsFilesystem)\n",
    "    hello_world_program = await squashfs_view.get_entry(\"src/hello_world\")\n",
    "\n",
    "    # Get the \"Hello, World!\" string location in the program and patch it with \"More meow!\"\n",
    "    program_data = await hello_world_program.resource.get_data()\n",
    "    hello_world_offset = program_data.find(b\"Hello, World!\")\n",
    "\n",
    "    await hello_world_program.resource.run(\n",
    "        BinaryPatchModifier, BinaryPatchConfig(hello_world_offset, b\"More meow!\\0\")\n",
    "    )\n",
    "\n",
    "    # Modify the program permission bits and xattrs before repacking:\n",
    "    # Misanthropic and eyebrow-raising permission bits...\n",
    "    await hello_world_program.modify_stat_attribute(stat.ST_MODE, 0o100300)\n",
    "    # ... And some useful extended attributes\n",
    "    await hello_world_program.modify_xattr_attribute(\"user.foo\", \"bar\".encode(\"utf-8\"))\n",
    "\n",
    "    await root_resource.pack()\n",
    "    await root_resource.flush_data_to_disk(output_filename)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "006eaa97",
   "metadata": {},
   "source": [
    "Does it work?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "af05cf0d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[      packer.py:   70] The SquashfsPacker packer has already been run on resource 4530acb5d25d4f979fe5a40dc1a541c0\n"
     ]
    }
   ],
   "source": [
    "await modify_squashfsed_program(basic_context, \"image.sqsh\", \"modified_image.sqsh\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "955ef900",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "More meow!\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "\n",
    "rm -rf modified_squashfs_dir && unsquashfs -d modified_squashfs_dir modified_image.sqsh >/dev/null\n",
    "./modified_squashfs_dir/src/hello_world"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "de1d2cfe",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "new permission bits: 100300\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "\n",
    "print(f\"new permission bits: {os.stat('modified_squashfs_dir/src/hello_world').st_mode:o}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "4f4e45d3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "new extended attributes: user.foo: bar\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "\n",
    "printf \"new extended attributes: \"\n",
    "xattr -l modified_squashfs_dir/src/hello_world"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25f9c89f",
   "metadata": {},
   "source": [
    "[Next page](6_code_insertion_with_extension.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.18"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "metadata": {
     "collapsed": false
    },
    "source": []
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
